use std::fmt::Debug;

trait Say {
    fn say(&self) -> &str {
        "say, hi!"
    }
}

trait Eat {
    fn eat(&self);
}

trait Mind{
  fn consider(&self) {
    println!("I am thinking...");
  }
}

trait Run {
    fn walk(&self)
    where
        Self: Debug;
}

#[derive(Debug)]
struct People;

impl Say for People {}

impl Eat for People {
    fn eat(&self) {
        println!("I like eat beef");
    }
}

impl Mind for People {}

#[derive(Debug)]
struct Cat;

impl Say for Cat {}

impl Eat for Cat {
    fn eat(&self) {
        println!("I like eat fish");
    }
}

// 通用实现<blanket implementation>，简单理解：在不需要知道特定类型情况下，仅用拥有某特质的类型，给具备这个特质的类型做特质扩展，类似ToString特质对所有实现了Display特质的类型的扩展
impl<T: Mind> Run for T {
    fn walk(&self)
    where
        Self: Debug,
    {
        println!("{:?} runing...", self);
    }
}

struct Point<T> {
    x: T,
    y: T,
}

// 只对特定类型进行实现（约束），其他类型无法实现
impl Point<i32> {
  fn mark(&self) {
    println!("coordinate mark");
  }
}

// 当需要处理单个参数单个特质时，特质对象的实现和泛型的实现约束差不多
fn call_single_trait(a: &impl Say) {
    println!("{}", a.say());
}

fn call_single_generic<T: Say>(a: &T) {
    println!("{}", a.say());
}

// 当需要处理多个参数不同特质时，特质对象的实现更为灵活，更为推荐
fn call_multi_trait(a: &impl Say, b: &impl Eat) {
    println!("{}", a.say());
    b.eat();
}

// 下面这种两个参数共用一个泛型约束，指明a和b参数是一个类型，类型必须实现Say特质
fn call_multi_generic<T: Say>(a: &T, b: &T) {
    println!("{}", a.say());
    println!("{}", b.say());
}

// 当需要处理单个参数多个特质时，特质对象的实现和泛型的实现差不多
fn call_many_trait(a: &(impl Say + Eat)) {
    println!("{}", a.say());
    a.eat();
}

fn call_trait_bound<T: Eat + Say>(a: &T) {
    println!("{}", a.say());
    a.eat();
}

// 将特质对象作为返回值，来处理编译时才能确定的类型
fn receive_life(value: bool) -> impl Eat + Say {
    People

    // 在这里不能返回不同类型的实例，即使它们实现了相同的特质
    // if value {
    //     People
    // } else {
    //     Cat
    // }
}

// 泛型绑定多特质，用where将特质约束提到函数末尾，提高代码可读性
// fn call_trait_bound<T>(a: &T)
// where
//     T: Eat + Say,
// {
//     println!("{}", a.say());
//     a.eat();
// }

fn main() {
    let p = People;
    let c = Cat;

    // 单参单特质
    call_single_trait(&p);
    call_single_generic(&p);

    // 多参单特质
    call_multi_trait(&p, &c);
    call_multi_generic(&p, &p);
    // call_multi_generic(&p, &c); // 编译失败

    // 单参多特质
    call_many_trait(&p);
    call_trait_bound(&p);

    // 通用实现
    p.walk();
    // c.walk(); // error
}
